// Includes
#include "InputDevice.h"

#include <cstring>		// C library string header

namespace EraAstral
{
	namespace core
	{
		// Input device constructor
		InputDevice::InputDevice()
		{
			curStateIndex = 0;
			// Create input states
			states = new InputState[2];
			
			// Zero out states
			ResetInputState(&states[0]);
			ResetInputState(&states[1]);
		}
		
		// Input device destructor
		InputDevice::~InputDevice()
		{
			delete[] states;
		}
		
		// Notify about a new frame
		void InputDevice::NotifyNewFrame()
		{		
			// Change index of current input state
			curStateIndex = !curStateIndex;
			
			// Copy data from old state to new state
			memcpy((void *)&states[curStateIndex], (void *)&states[!curStateIndex],
						sizeof(InputState));
		}
		
		// Event processing
		void InputDevice::ProcessEventSignal(const Event *event)
		{
			// Pointers to data
			SDLKey *keyPtr = 0;
			SDLMod *modPtr = 0;
			Uint16 *mouseMovePtr = 0;
			Uint8 *btnPtr = 0;
		
			// Check event type
			switch (event->type)
			{
				// Key press event
				case EVENT_KEY_PRESS:
					// Get data from event
					keyPtr = (SDLKey *)event->data;
					modPtr = (SDLMod *)event->data;
					modPtr++;
					// Set key flag for current input state
					states[curStateIndex].keyboard.keys[*keyPtr] = true;
					states[curStateIndex].keyboard.mod = *modPtr;
					break;
					
				// Key release event
				case EVENT_KEY_RELEASE:
					// Get data from event
					keyPtr = (SDLKey *)event->data;
					modPtr = (SDLMod *)event->data;
					modPtr++;
					// Set key flag for current input state
					states[curStateIndex].keyboard.keys[*keyPtr] = false;
					states[curStateIndex].keyboard.mod = *modPtr;
					break;
					
				// Mouse movement event
				case EVENT_MOUSE_MOVE:
					// Get data from event
					mouseMovePtr = (Uint16 *)event->data;
					states[curStateIndex].mouse.position[0] = *mouseMovePtr;
					mouseMovePtr++;
					states[curStateIndex].mouse.position[1] = *mouseMovePtr;
					break;
					
				// Mouse button press event
				case EVENT_BUTTON_PRESS:
					// Get data from event
					btnPtr = (Uint8 *)event->data;
					// Set mouse button flag
					states[curStateIndex].mouse.buttons[*btnPtr] = true;
					break;
					
				// Mouse button press event
				case EVENT_BUTTON_RELEASE:
					// Get data from event
					btnPtr = (Uint8 *)event->data;
					// Clear mouse button flag
					states[curStateIndex].mouse.buttons[*btnPtr] = false;
					break;
					
				// Event type not processed by input device
				default:
					break;
			}
			
			// Check for mouse wheel movement
			if (states[curStateIndex].mouse.buttons[SDL_BUTTON_WHEELUP])
				states[curStateIndex].mouse.wheel += 0.1f;
			if (states[curStateIndex].mouse.buttons[SDL_BUTTON_WHEELDOWN])
				states[curStateIndex].mouse.wheel -= 0.1f;
				
			// Clear wheel state
			states[curStateIndex].mouse.buttons[SDL_BUTTON_WHEELUP] = false;
			states[curStateIndex].mouse.buttons[SDL_BUTTON_WHEELDOWN] = false;
		}
		
		// Resets an input state
		void InputDevice::ResetInputState(InputState *state)
		{
			// Reset keys
			for (int i = 0; i < SDLK_LAST; i++)
				state->keyboard.keys[i] = false;
			
			// Reset key modifier
			state->keyboard.mod = KMOD_NONE;
			
			// Reset mouse position
			state->mouse.position[0] = 0;
			state->mouse.position[1] = 0;
			
			// Reset mouse buttons
			for (int i = 0; i < MOUSE_BUTTONS_MAX; i++)
				state->mouse.buttons[i] = false;
				
			// Reset mouse wheel
			state->mouse.wheel = 0.0f;
		}
		
		// Check if a key is pressed down
		bool InputDevice::IsKeyDown(SDLKey keyCode)
		{
			return states[curStateIndex].keyboard.keys[keyCode];
		}
		
		// Check if a key is released
		bool InputDevice::IsKeyUp(SDLKey keyCode)
		{
			return !IsKeyDown(keyCode);
		}
		
		// Check if a key was pressed down last frame
		bool InputDevice::WasKeyDown(SDLKey keyCode)
		{
			return states[!curStateIndex].keyboard.keys[keyCode];
		}
		
		// Check if a key was up last frame
		bool InputDevice::WasKeyUp(SDLKey keyCode)
		{
			return !WasKeyDown(keyCode);
		}
		
		// Get X component of mouse position
		int InputDevice::GetMouseX() const
		{
			return states[curStateIndex].mouse.position[0];
		}
		// Get Y component of mouse position
		int InputDevice::GetMouseY() const
		{
			return states[curStateIndex].mouse.position[1];
		}
	}
}
